package cn.edu.tju.wallet.domain;

import cn.edu.tju.core.model.BaseEntity;
import cn.edu.tju.wallet.domain.RechargeRule;
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

/**
 * 虚拟钱包领域模型
 */
@Entity
@Table(name = "wallets")
public class VirtualWallet extends BaseEntity {

    @Column(name = "user_id", nullable = false, unique = true)
    private Long userId; // 用户ID

    @Column(name = "balance", precision = 19, scale = 2, nullable = false)
    private BigDecimal balance = BigDecimal.ZERO; // 总余额

    @Column(name = "available_balance", precision = 19, scale = 2, nullable = false)
    private BigDecimal availableBalance = BigDecimal.ZERO; // 可用余额

    @Column(name = "frozen_balance", precision = 19, scale = 2, nullable = false)
    private BigDecimal frozenBalance = BigDecimal.ZERO; // 冻结余额

    @Column(name = "principal_balance", precision = 19, scale = 2, nullable = false)
    private BigDecimal principalBalance = BigDecimal.ZERO; // 本金余额

    @Column(name = "bonus_balance", precision = 19, scale = 2, nullable = false)
    private BigDecimal bonusBalance = BigDecimal.ZERO; // 奖励余额

    @Column(name = "overdraft_limit", precision = 19, scale = 2, nullable = false)
    private BigDecimal overdraftLimit = BigDecimal.ZERO; // 透支额度

    @Column(name = "overdraft_amount", precision = 19, scale = 2, nullable = false)
    private BigDecimal overdraftAmount = BigDecimal.ZERO; // 已透支金额

    @Column(name = "overdraft_date")
    private LocalDateTime overdraftDate; // 透支开始日期

    @Column(name = "vip_level", nullable = false)
    private Integer vipLevel = 0; // VIP等级

    @Version
    @Column(name = "version")
    private Integer version = 0; // 乐观锁版本号

    public VirtualWallet() {}

    public VirtualWallet(Long userId) {
        this.userId = userId;
        this.balance = BigDecimal.ZERO;
        this.availableBalance = BigDecimal.ZERO;
        this.frozenBalance = BigDecimal.ZERO;
        this.principalBalance = BigDecimal.ZERO;
        this.bonusBalance = BigDecimal.ZERO;
        this.overdraftLimit = BigDecimal.ZERO;
        this.overdraftAmount = BigDecimal.ZERO;
        this.vipLevel = 0;
        this.setCreateTime(LocalDateTime.now());
        this.setUpdateTime(LocalDateTime.now());
    }

    // ============================================
    // 核心业务方法
    // ============================================

    /**
     * 充值功能
     * @param amount 充值金额
     * @param rule 充值规则
     * @return 充值结果
     */
    public DepositResult deposit(BigDecimal amount, RechargeRule rule) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0)
            throw new IllegalArgumentException("充值金额必须大于0");

        // 计算奖励金额
        BigDecimal bonus = calculateBonus(amount, rule);

        // 更新余额（充值金额 + 奖励金额）
        this.principalBalance = this.principalBalance.add(amount);
        this.bonusBalance = this.bonusBalance.add(bonus);
        this.availableBalance = this.availableBalance.add(amount).add(bonus);
        this.balance = this.balance.add(amount).add(bonus);

        this.setUpdateTime(LocalDateTime.now());

        return new DepositResult(amount, bonus, this.balance);
    }

    /**
     * 提现功能
     * @param amount 提现金额
     * @param rule 提现规则
     * @return 提现结果
     */
    public WithdrawResult withdraw(BigDecimal amount, WithdrawRule rule) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0)
            throw new IllegalArgumentException("提现金额必须大于0");

        // 计算手续费
        BigDecimal fee = calculateFee(amount, rule);
        BigDecimal totalDeduct = amount.add(fee);

        // 检查可用余额
        if (this.availableBalance.compareTo(totalDeduct) < 0)
            throw new IllegalStateException("可用余额不足");

        // 按比例扣除本金和奖励
        BigDecimal totalBalance = this.principalBalance.add(this.bonusBalance);
        if (totalBalance.compareTo(BigDecimal.ZERO) == 0)
            throw new IllegalStateException("账户余额为0");

        BigDecimal principalRatio = this.principalBalance.divide(totalBalance, 4, RoundingMode.HALF_UP);
        BigDecimal bonusRatio = this.bonusBalance.divide(totalBalance, 4, RoundingMode.HALF_UP);

        BigDecimal principalDeduct = totalDeduct.multiply(principalRatio).setScale(2, RoundingMode.HALF_UP);
        BigDecimal bonusDeduct = totalDeduct.multiply(bonusRatio).setScale(2, RoundingMode.HALF_UP);

        // 更新余额
        this.principalBalance = this.principalBalance.subtract(principalDeduct);
        this.bonusBalance = this.bonusBalance.subtract(bonusDeduct);
        this.availableBalance = this.availableBalance.subtract(totalDeduct);
        this.balance = this.balance.subtract(totalDeduct);

        this.setUpdateTime(LocalDateTime.now());

        return new WithdrawResult(amount, fee, amount, principalDeduct, bonusDeduct);
    }

    /**
     * 转账功能（支付）
     * @param toWallet 收款方钱包
     * @param amount 转账金额
     * @param allowOverdraft 是否允许使用透支
     * @return 转账结果
     */
    public TransferResult transfer(VirtualWallet toWallet, BigDecimal amount, boolean allowOverdraft) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0)
            throw new IllegalArgumentException("转账金额必须大于0");

        if (toWallet == null)
            throw new IllegalArgumentException("收款方钱包不能为空");

        // 检查付款方余额
        if (this.availableBalance.compareTo(amount) >= 0) {
            // 余额充足，直接扣款
            this.availableBalance = this.availableBalance.subtract(amount);
            this.balance = this.balance.subtract(amount);
        }
        else {
            // 余额不足，检查是否允许透支
            if (!allowOverdraft) {
                throw new IllegalStateException("余额不足，支付失败。如需使用透支，请设置allowOverdraft=true");
            }

            BigDecimal shortage = amount.subtract(this.availableBalance);

            // 检查透支额度
            BigDecimal maxOverdraft = this.overdraftLimit.subtract(this.overdraftAmount);
            if (shortage.compareTo(maxOverdraft) > 0) {
                throw new IllegalStateException(
                    String.format("余额不足且超过透支额度。可用余额: %s, 需补足: %s, 剩余透支额度: %s",
                        this.availableBalance, shortage, maxOverdraft));
            }

            // 使用透支
            this.availableBalance = BigDecimal.ZERO;
            this.balance = this.balance.subtract(amount);
            this.overdraftAmount = this.overdraftAmount.add(shortage);
            if (this.overdraftDate == null) this.overdraftDate = LocalDateTime.now();
        }

        // 收款方收款（冻结状态）
        toWallet.frozenBalance = toWallet.frozenBalance.add(amount);
        toWallet.balance = toWallet.balance.add(amount);

        this.setUpdateTime(LocalDateTime.now());
        toWallet.setUpdateTime(LocalDateTime.now());

        return new TransferResult(amount, this.balance, toWallet.balance);
    }

    /**
     * 转账功能（支付）- 兼容旧版本，默认不允许透支
     * @param toWallet 收款方钱包
     * @param amount 转账金额
     * @return 转账结果
     */
    public TransferResult transfer(VirtualWallet toWallet, BigDecimal amount) {
        return transfer(toWallet, amount, false);
    }

    /**
     * 冻结资金
     * @param amount 冻结金额
     * @return 是否成功
     */
    public boolean freeze(BigDecimal amount) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0)
            throw new IllegalArgumentException("冻结金额必须大于0");

        if (this.availableBalance.compareTo(amount) < 0)
            throw new IllegalStateException("可用余额不足");

        this.availableBalance = this.availableBalance.subtract(amount);
        this.frozenBalance = this.frozenBalance.add(amount);
        this.setUpdateTime(LocalDateTime.now());

        return true;
    }

    /**
     * 解冻资金
     * @param amount 解冻金额
     * @return 是否成功
     */
    public boolean unfreeze(BigDecimal amount) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0)
            throw new IllegalArgumentException("解冻金额必须大于0");

        if (this.frozenBalance.compareTo(amount) < 0)
            throw new IllegalStateException("冻结余额不足");

        this.frozenBalance = this.frozenBalance.subtract(amount);
        this.availableBalance = this.availableBalance.add(amount);
        this.setUpdateTime(LocalDateTime.now());

        return true;
    }

    /**
     * 透支功能
     * @param amount 透支金额
     * @param rule 透支规则
     * @return 是否成功
     */
    public boolean overdraft(BigDecimal amount, OverdraftRule rule) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0)
            throw new IllegalArgumentException("透支金额必须大于0");

        // 检查VIP等级
        if (this.vipLevel < rule.getMinVipLevel())
            throw new IllegalStateException("VIP等级不足，无法透支");

        // 检查透支额度
        BigDecimal newOverdraftAmount = this.overdraftAmount.add(amount);
        if (newOverdraftAmount.compareTo(rule.getOverdraftLimit()) > 0)
            throw new IllegalStateException("超过透支额度");

        this.overdraftAmount = newOverdraftAmount;
        this.availableBalance = this.availableBalance.add(amount);
        if (this.overdraftDate == null)
            this.overdraftDate = LocalDateTime.now();
        this.setUpdateTime(LocalDateTime.now());

        return true;
    }

    /**
     * 还款功能
     * @param amount 还款金额
     * @param rule 透支规则
     * @return 还款结果
     */
    public RepayResult repay(BigDecimal amount, OverdraftRule rule) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0)
            throw new IllegalArgumentException("还款金额必须大于0");

        if (this.overdraftAmount.compareTo(BigDecimal.ZERO) == 0)
            throw new IllegalStateException("无透支金额，无需还款");

        // 计算利息
        BigDecimal interest = calculateInterest(this.overdraftAmount, rule);
        BigDecimal totalDebt = this.overdraftAmount.add(interest);

        BigDecimal repaidPrincipal;
        BigDecimal repaidInterest;
        BigDecimal remainingOverdraft;
        BigDecimal excessAmount = BigDecimal.ZERO;

        if (amount.compareTo(totalDebt) >= 0) {
            // 全额还款
            repaidPrincipal = this.overdraftAmount;
            repaidInterest = interest;
            remainingOverdraft = BigDecimal.ZERO;
            excessAmount = amount.subtract(totalDebt);
            
            this.overdraftAmount = BigDecimal.ZERO;
            this.overdraftDate = null;
            this.availableBalance = this.availableBalance.add(excessAmount);
        } 
        else if (amount.compareTo(interest) > 0) {
            // 部分还款：先扣利息，余额抵本金
            repaidInterest = interest;
            repaidPrincipal = amount.subtract(interest);
            this.overdraftAmount = this.overdraftAmount.subtract(repaidPrincipal);
            remainingOverdraft = this.overdraftAmount;
            
        } 
        else {
            // 仅还利息
            repaidInterest = amount;
            repaidPrincipal = BigDecimal.ZERO;
            remainingOverdraft = this.overdraftAmount;
        }

        this.setUpdateTime(LocalDateTime.now());

        return new RepayResult(repaidPrincipal, repaidInterest, remainingOverdraft, excessAmount);
    }

    // ============================================
    // 辅助方法
    // ============================================

    /**
     * 检查是否可以支付
     * @param amount 支付金额
     * @return 是否可以支付
     */
    public boolean canPay(BigDecimal amount) {
        BigDecimal maxPayable = this.availableBalance.add(this.overdraftLimit).subtract(this.overdraftAmount);
        return maxPayable.compareTo(amount) >= 0;
    }

    /**
     * 获取可支付金额
     * @return 可支付金额
     */
    public BigDecimal getAvailableAmount() {
        return this.availableBalance.add(this.overdraftLimit).subtract(this.overdraftAmount);
    }

    /**
     * 计算充值奖励
     * @param amount 充值金额
     * @param rule 充值规则
     * @return 奖励金额
     */
    public BigDecimal calculateBonus(BigDecimal amount, RechargeRule rule) {
        if (rule == null) return BigDecimal.ZERO;

        if (amount.compareTo(rule.getMinAmount()) < 0)
            return BigDecimal.ZERO;

        // 优先使用固定奖励金额
        if (rule.getBonusAmount() != null && rule.getBonusAmount().compareTo(BigDecimal.ZERO) > 0)
            return rule.getBonusAmount();

        // 使用比例奖励
        if (rule.getBonusRate() != null && rule.getBonusRate().compareTo(BigDecimal.ZERO) > 0)
            return amount.multiply(rule.getBonusRate()).setScale(2, RoundingMode.HALF_UP);

        return BigDecimal.ZERO;
    }

    /**
     * 计算提现手续费
     * @param amount 提现金额
     * @param rule 提现规则
     * @return 手续费
     */
    public BigDecimal calculateFee(BigDecimal amount, WithdrawRule rule) {
        if (rule == null) return BigDecimal.ZERO;

        BigDecimal fee = amount.multiply(rule.getFeeRate()).setScale(2, RoundingMode.HALF_UP);

        // 应用最低手续费
        if (rule.getMinFee() != null && fee.compareTo(rule.getMinFee()) < 0)
            fee = rule.getMinFee();

        // 应用最高手续费
        if (rule.getMaxFee() != null && fee.compareTo(rule.getMaxFee()) > 0)
            fee = rule.getMaxFee();

        return fee;
    }

    /**
     * 计算透支利息
     * 
     * @param overdraftAmount 透支金额
     * @param rule 透支规则
     * @return 利息
     */
    public BigDecimal calculateInterest(BigDecimal overdraftAmount, OverdraftRule rule) {
        if (overdraftAmount == null || overdraftAmount.compareTo(BigDecimal.ZERO) == 0)
            return BigDecimal.ZERO;

        if (this.overdraftDate == null) return BigDecimal.ZERO;

        // 计算透支天数
        long days = ChronoUnit.DAYS.between(this.overdraftDate, LocalDateTime.now());
        
        // 扣除免息期
        if (rule.getGracePeriodDays() != null && days <= rule.getGracePeriodDays())
            return BigDecimal.ZERO;

        long chargeableDays = days - (rule.getGracePeriodDays() != null ? rule.getGracePeriodDays() : 0);

        // 计算利息：本金 × 日利率 × 天数
        BigDecimal interest = overdraftAmount
                .multiply(rule.getDailyInterestRate())
                .multiply(BigDecimal.valueOf(chargeableDays))
                .setScale(2, RoundingMode.HALF_UP);

        return interest;
    }

    // ============================================
    // Getter and Setter
    // ============================================

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public BigDecimal getBalance() {
        return balance;
    }

    public void setBalance(BigDecimal balance) {
        this.balance = balance;
    }

    public BigDecimal getAvailableBalance() {
        return availableBalance;
    }

    public void setAvailableBalance(BigDecimal availableBalance) {
        this.availableBalance = availableBalance;
    }

    public BigDecimal getFrozenBalance() {
        return frozenBalance;
    }

    public void setFrozenBalance(BigDecimal frozenBalance) {
        this.frozenBalance = frozenBalance;
    }

    public BigDecimal getPrincipalBalance() {
        return principalBalance;
    }

    public void setPrincipalBalance(BigDecimal principalBalance) {
        this.principalBalance = principalBalance;
    }

    public BigDecimal getBonusBalance() {
        return bonusBalance;
    }

    public void setBonusBalance(BigDecimal bonusBalance) {
        this.bonusBalance = bonusBalance;
    }

    public BigDecimal getOverdraftLimit() {
        return overdraftLimit;
    }

    public void setOverdraftLimit(BigDecimal overdraftLimit) {
        this.overdraftLimit = overdraftLimit;
    }

    public BigDecimal getOverdraftAmount() {
        return overdraftAmount;
    }

    public void setOverdraftAmount(BigDecimal overdraftAmount) {
        this.overdraftAmount = overdraftAmount;
    }

    public LocalDateTime getOverdraftDate() {
        return overdraftDate;
    }

    public void setOverdraftDate(LocalDateTime overdraftDate) {
        this.overdraftDate = overdraftDate;
    }

    public Integer getVipLevel() {
        return vipLevel;
    }

    public void setVipLevel(Integer vipLevel) {
        this.vipLevel = vipLevel;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    // ============================================
    // 内部结果类
    // ============================================

    // 充值结果类
    public static class DepositResult {
        private BigDecimal amount;
        private BigDecimal bonus;
        private BigDecimal newBalance;

        public DepositResult(BigDecimal amount, BigDecimal bonus, BigDecimal newBalance) {
            this.amount = amount;
            this.bonus = bonus;
            this.newBalance = newBalance;
        }

        public BigDecimal getAmount() {
            return amount;
        }

        public BigDecimal getBonus() {
            return bonus;
        }

        public BigDecimal getNewBalance() {
            return newBalance;
        }
    }

    // 提现结果
    public static class WithdrawResult {
        private BigDecimal amount;
        private BigDecimal fee;
        private BigDecimal actualAmount;
        private BigDecimal principalDeduct;
        private BigDecimal bonusDeduct;

        public WithdrawResult(BigDecimal amount, BigDecimal fee, BigDecimal actualAmount, 
                            BigDecimal principalDeduct, BigDecimal bonusDeduct) {
            this.amount = amount;
            this.fee = fee;
            this.actualAmount = actualAmount;
            this.principalDeduct = principalDeduct;
            this.bonusDeduct = bonusDeduct;
        }

        public BigDecimal getAmount() {
            return amount;
        }

        public BigDecimal getFee() {
            return fee;
        }

        public BigDecimal getActualAmount() {
            return actualAmount;
        }

        public BigDecimal getPrincipalDeduct() {
            return principalDeduct;
        }

        public BigDecimal getBonusDeduct() {
            return bonusDeduct;
        }
    }

    // 转账结果
    public static class TransferResult {
        private BigDecimal amount;
        private BigDecimal payerNewBalance;
        private BigDecimal receiverNewBalance;

        public TransferResult(BigDecimal amount, BigDecimal payerNewBalance, BigDecimal receiverNewBalance) {
            this.amount = amount;
            this.payerNewBalance = payerNewBalance;
            this.receiverNewBalance = receiverNewBalance;
        }

        public BigDecimal getAmount() {
            return amount;
        }

        public BigDecimal getPayerNewBalance() {
            return payerNewBalance;
        }

        public BigDecimal getReceiverNewBalance() {
            return receiverNewBalance;
        }
    }

    // 还款结果
    public static class RepayResult {
        private BigDecimal repaidPrincipal;
        private BigDecimal repaidInterest;
        private BigDecimal remainingOverdraft;
        private BigDecimal excessAmount;

        public RepayResult(BigDecimal repaidPrincipal, BigDecimal repaidInterest, 
                         BigDecimal remainingOverdraft, BigDecimal excessAmount) {
            this.repaidPrincipal = repaidPrincipal;
            this.repaidInterest = repaidInterest;
            this.remainingOverdraft = remainingOverdraft;
            this.excessAmount = excessAmount;
        }

        public BigDecimal getRepaidPrincipal() {
            return repaidPrincipal;
        }

        public BigDecimal getRepaidInterest() {
            return repaidInterest;
        }

        public BigDecimal getRemainingOverdraft() {
            return remainingOverdraft;
        }

        public BigDecimal getExcessAmount() {
            return excessAmount;
        }
    }
}

